#pragma once

#include "type.h"

#include "utility.h"

#include "defaultworkqueue.h"

namespace RenderSystem
{
	/**
	 * A map of shader parameter names to registers allocated to that parameter.( used for shared parameter types )
	 */
	class ShaderParameterMap
	{
	public:
		
		bool FindParameterAllocation(const TCHAR* pszParameterName,WORD& rOutBufferIndex,WORD& OutBaseIndex,WORD& OutSize,WORD& OutSamplerIndex) const;
		void AddParameterAllocation(const TCHAR* pszParameterName,WORD BufferIndex,WORD BaseIndex,WORD Size,WORD SamplerIndex);

		DWORD GetCRC() const;

	private:

		struct ParameterAllocation
		{
			WORD BufferIndex;
			WORD BaseIndex;
			WORD Size;
			WORD SamplerIndex;
		};

		typedef std::map<String, ParameterAllocation>			IdParamMap;
		typedef IdParamMap::iterator							IdParamMapIterator;
		typedef IdParamMap::const_iterator						IdParamMapConstIterator;

		IdParamMap			m_ParameterMap;
	};

	struct ShaderProfile
	{
		enum ShaderModel
		{
			eSM_1_1,
			eSM_2_0,
			eSM_3_0,
			eSM_4_0,
			eSM_End
		};

		enum Frequency
		{
			eVS,
			ePS,
			eGS,
			eF_End
		};

		static String strFrequency[ShaderProfile::eF_End];
		static String strShaderModel[ShaderProfile::eSM_End];

		ShaderModel	m_SM;
		Frequency	m_Frequency;

		ShaderProfile( ShaderModel eSM, Frequency eFrequency )
			:m_SM(eSM), m_Frequency(eFrequency)
		{
		}

		String GetProfile( void ) const
		{
			assert( m_Frequency < ShaderProfile::eF_End && m_SM < ShaderProfile::eSM_End );
			return strFrequency[m_Frequency] + strShaderModel[m_SM];
		}
	};

	/**
	 * The environment used to compile a shader.
	 */
	struct ShaderCompilerEnvironment
	{
		typedef std::map<String, String>	CompileMap;
		typedef CompileMap::iterator		CompileMapIterator;
		typedef CompileMap::const_iterator	CompileMapConstIterator;

		//map include file name to the contents
		CompileMap	IncludeFiles;
		//map the definitions to the value
		CompileMap	Definitions;
	};


	/**
	* The output of the shader compiler.
	*/
	struct ShaderCompilerOutput
	{
		ShaderCompilerOutput():
	m_NumInstructions(0)
	{}

	ShaderParameterMap	m_ParameterMap;
	UINT				m_NumInstructions;
	
	String				m_strCodes;
	};
	typedef std::vector<ShaderCompilerOutput*>		ShaderCompilerOutputs;
	typedef ShaderCompilerOutputs::iterator			ShaderCompilerOutputsIterator;
	typedef ShaderCompilerOutputs::const_iterator	ShaderCompilerOutputsConstIterator;

	/** Stores all of the input and output information used to compile a single shader. */
	//replace with my work queue
	struct ShaderCompileJob
	{
		/** Vertex factory type that this shader belongs to, may be NULL */
		class VertexFactoryType* pVFType;
		/** Shader type that this shader belongs to, must be valid */
		class ShaderType* pShaderType;
		/** Input for the shader compile */
		String szSourceFilename;
		String szFunctionName;
		ShaderProfile objShaderProfile;
		ShaderCompilerEnvironment objEnvironment;
		
		/** Output of the shader compile */
		bool bSucceeded;
		ShaderCompilerOutput objOutput;

		ShaderCompileJob(
			VertexFactoryType* pInVFType,
			ShaderType* pInShaderType,
			const TCHAR* pszInSourceFilename,
			const TCHAR* pszInFunctionName,
			ShaderProfile objShaderProfile,
			const ShaderCompilerEnvironment& rInEnvironment) 
			:
		pVFType(pInVFType),
			pShaderType(pInShaderType),
			szSourceFilename(pszInSourceFilename),
			szFunctionName(pszInFunctionName),
			objShaderProfile(objShaderProfile),
			objEnvironment(rInEnvironment),
			bSucceeded(FALSE)
		{
		}
	};

	typedef std::vector<ShaderCompileJob>		ShaderCompileJobs;
	typedef ShaderCompileJobs::iterator			ShaderCompileJobsIterator;
	typedef ShaderCompileJobs::const_iterator	ShaderCompileJobsConstIterator;

	class ShaderCompiler
		:public Common::WorkQueue::RequestHandler
	{
	public:

		typedef std::map< String, String >			ShaderFileCaches;
		typedef ShaderFileCaches::iterator			ShaderFileCachesIterator;
		typedef ShaderFileCaches::const_iterator	ShaderFileCachesConstIterator;

		ShaderCompiler( void );
		virtual ~ShaderCompiler( void );
		
		//Workqueue interface
		virtual Common::WorkQueue::Response* handleRequest(const Common::WorkQueue::Request* req, const Common::WorkQueue* srcQ);

		String LoadShaderSourceFile( const TCHAR* pszFileName );

	protected:

		virtual bool _CompileShader( 
			const TCHAR* pszSourceFilename,
			const TCHAR* pszFunctionName,
			const ShaderCompilerEnvironment& rEnvironment,
			const ShaderProfile& rShaderProfile, 
			ShaderCompilerOutput& rOutput ) = 0;

	protected:

		ShaderFileCaches				m_ShaderFileCaches;
	};

	class ShaderCompilerManager
		:public Singleton< ShaderCompilerManager >
	{
	public:
		
		ShaderCompilerManager( void );
		virtual ~ShaderCompilerManager( void );

		void AddCompileJob( const ShaderCompileJob& rCompileJob );
		ShaderCompileJobs& GetCurrentJob( void );
		ShaderCompiler& GetShaderCompiler( void ) const;

	private:

		ShaderCompiler&					m_rShaderCompiler;

		uint16							m_uCompileChannel;
		Common::DefaultWorkQueue		m_CompileWorkQueue;


		ShaderCompileJobs				m_CurJobs;
	};
}